#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <poll.h>
#include <sys/epoll.h>
#include <pthread.h>

#include "Epoller.h"

#include <iostream>
#include <string>
#include <memory>
#include <thread>
#include <atomic>
#include <functional>

using namespace std;


// select系统调用：改进的多客户/服务器
#define MAXFD                          10
#define _TEST_EPOLL_SINGLE             0
#define _TEST_EPOLL_COMPLEX            1
#define _TEST_SELECT_COMPLEX           0
#define _TEST_SELECT_SINGLE            0


#if ( _TEST_EPOLL_COMPLEX == 1 )


bool m_isExit = false;


void *threadFunc(void *arg)
{
    printf("Run threadFunc.\n");

    Epoller *pArg = static_cast<Epoller*>(arg);

    char buf[512] = {0};
    std::list<int> fdlist;

    while( !m_isExit )
    {
        pArg->getSockfds(fdlist);

        auto iter = fdlist.begin();

        while ( iter != fdlist.end() )
        {
            if ( (*iter) < 0 ){
                iter++;
                continue;
            }

            if ( (*iter) == STDIN_FILENO )
            {
                memset(buf, 0, 512);
                int readLen = read((*iter), buf, 512);
                if ( readLen > 0 )
                {
                    printf("==recv data: %s\n\n", buf);
                }
                iter++;
            }
        }

    }

    printf("Thread Exit.\n");

    return nullptr;
}


void threadRead(std::unique_ptr<Epoller> &ptr)
{

    printf("%s | %d\n", __FUNCTION__, __LINE__);

    std::unique_ptr<Epoller> pArg(ptr.release());

    char buf[512] = {0};
    std::list<int> fdlist;


    while( !m_isExit )
    {
        pArg->getSockfds(fdlist);

        auto iter = fdlist.begin();

        while ( iter != fdlist.end() )
        {
            if ( (*iter) < 0 ){
                iter++;
                continue;
            }

            if ( (*iter) == STDIN_FILENO )
            {
                memset(buf, 0, 512);
                int readLen = read((*iter), buf, 512);
                if ( readLen > 0 )
                {
                    printf("recv data: %s\n\n", buf);
                }
                iter++;
            }
        }

    }
}


int main()
{

    printf("=================Start==================\n");
    std::unique_ptr<Epoller> pEpoller(new Epoller(1000,10));

    // 添加标准输入作为描述符
    pEpoller->add(STDIN_FILENO);

    //std::ref强制转换成引用，可配合引用参数使用
    std::thread th = std::thread(std::bind(threadRead, std::ref(pEpoller)));


    while(1)
    {
        sleep(1);
    }

    m_isExit = false;

    th.join();
    pEpoller->remove(STDIN_FILENO);


#if 0
    Epoller *pEpoller = new Epoller(1000,10);
    // 添加标准输入作为描述符
    pEpoller->add(STDIN_FILENO);

    pthread_t thread;
    int res = pthread_create(&thread, nullptr, threadFunc, (void*)pEpoller);
    if ( res < 0 )
    {
        printf("pthread_create fail, error: %s\n", strerror(errno));
    }

    while(1)
    {
        sleep(1);
    }

    m_isExit = true;

    pEpoller->remove(STDIN_FILENO);

    delete pEpoller;
    pEpoller = nullptr;

    pthread_join(thread, nullptr);
#endif


    return 0;
}



#endif





#if ( _TEST_EPOLL_SINGLE == 1 )

#define MAX_LEN 256
#define EPOLL_SIZE  1000

int main()
{
    int     epfd = 0;
    int     len = 0;
    int     nfds = 0;
    int     index = 0;
    char    buf[MAX_LEN] = {0};
    struct  epoll_event ev;
    struct  epoll_event event[EPOLL_SIZE];

    //create a epoll object
    if(-1==(epfd=epoll_create(EPOLL_SIZE)))
    {
       perror("create epoll object error\n");
       return  -1;
    }

    printf("epoll create success.\n");


    //add a file descriptor which need to be listened
    ev.events = EPOLLIN | EPOLLET;          // 事件
    ev.data.fd= STDIN_FILENO;               // 标准输入描述符
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);


    // 开始监听
    while (1)
    {
        nfds = epoll_wait(epfd, event, EPOLL_SIZE, -1);
        if ( nfds < 0 )
        {
            perror("epoll_wait wait error.\n");
        }


        printf("nfds: %d\n", nfds);


        for ( auto i=0; i<nfds; i++ )
        {
            if ( event[i].events == EPOLLIN )
            {
                memset(buf, 0, MAX_LEN);
                len = read(event[index].data.fd,buf,sizeof(buf));
                printf("recv data: %s\n", buf);
                printf("len: %d\n\n", len);
            }
        }
    }
    return 0;
}


#endif



#if ( _TEST_SELECT_COMPLEX == 1 )

/* 向集合fds中添加文件描述符fd */
void fds_add(int fds[], int fd)
{
    int i = 0;
    for (; i < MAXFD; i++)
    {
        printf("i: %d | fds: %d\n", i, fds[i]);
        if (fds[i] == -1)
        {
            fds[i] = fd;
            break;               // 将未初始化的元素进行初始化
        }
    }
}

/* 在集合fds中删除文件描述符fd */
void fds_del(int fds[], int fd)
{
    int i = 0;
    for (; i < MAXFD; i++)
    {
        if (fds[i] == fd)
        {
            fds[i] = -1;
            break;
        }
    }
}

int main()
{
    /* 创建TCP监听套接字（socket描述符），指定协议族ipv4，字节流服务传输 */
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(sockfd != -1);

    /* socket专用地址信息设置 */
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    /* 命名套接字，将socket专用地址绑定到socket描述符上 */
    int res = bind(sockfd, (struct sockaddr*) & saddr, sizeof(saddr));
    assert(res != -1);

    /* 创建监听队列 */
    listen(sockfd, 5);

    fd_set fdset;

    /* 初始化文件描述符集合fds */
    int fds[MAXFD];
    int i = 0;
    for (; i < MAXFD; i++)
    {
        fds[i] = -1;
    }

    /* 将sockfd添加到文件描述符集合fds中 */
    fds_add(fds, sockfd);

    while (1)
    {
        /* fdset初始化为空集合，清除fdset的所有位 */
        FD_ZERO(&fdset);

        int maxfd = -1;
        int i = 0;
        /* 循环遍历找到最大的文件描述符 */
        for (; i < MAXFD; i++)
        {
            if (fds[i] == -1)
            {
                continue;
            }

            /* 设置fdset的fds[i]位 */
            FD_SET(fds[i], &fdset);

            if (fds[i] > maxfd)
            {
                maxfd = fds[i];             // 没有客户端连接时，maxfd的值为socketfd;
            }
        }

        /* 设置超时时间为5秒 */
        struct timeval tv = { 5,0 };

        /* 用n来接收select成功时返回就绪文件描述符的总数 */
        int n = select(maxfd + 1, &fdset, NULL, NULL, &tv);

        /* select失败返回-1并设置errno */
        if (n == -1)
        {
            perror("select error\n");
        }
        /* select为0表示文件描述符都没有变化，那么打印出超时信息 */
        else if (n == 0)
        {
            printf("time out\n");
        }
        else
        {
            /* 遍历所有可能的文件描述符，以检查是哪个上面有活动发生。*/
            for (i = 0; i < MAXFD; i++)
            {
                if (fds[i] == -1)
                {
                    continue;
                }

                /* fdset的fds[i]位已经被标识，即有数据 */
                if (FD_ISSET(fds[i], &fdset))
                {
                    /*
                    **	此时有两种情况，若fds[i] == sockfd
                    **	说明监听队列中有连接待处理，则使用accept拿出一个连接, 并且加入到select的文件描述符的集合里面
                    **	否则，没有新连接，我们直接使用recv接收客端数据，并打印
                    */
                    if (fds[i] == sockfd)
                    {
                        struct sockaddr_in caddr;
                        int len = sizeof(caddr);

                        /* 接收一个套接字已建立的连接，得到连接套接字connfd */
                        int connfd = accept(sockfd, (struct sockaddr*) & caddr, (socklen_t *)&len);
                        if (connfd < 0)
                        {
                            continue;
                        }

                        printf("accept connfd=%d\n", connfd);

                        /* 将连接套接字connfd，添加到fds文件描述集合中 */
                        fds_add(fds, connfd);
                    }
                    else
                    {

                        char buff[128] = { 0 };

                        /* recv用来接收客端数据*/
                        int res = recv(fds[i], buff, 127, 0);

                        /* 接收服务器端的数据是零，即res返回0，说明客户端已经关闭 */
                        if (res <= 0)
                        {
                            /* 关闭文件描述符fds[i] */
                            close(fds[i]);

                            /* 清除fds数组 */
                            fds_del(fds, fds[i]);
                            printf("one client over\n");
                        }
                        else
                        {
                            /* 输出客端发来的数据，并向客端发送一个OK的回复 */
                            printf("recv(%d)=%s\n", fds[i], buff);
                            send(fds[i], "ok", 2, 0);
                        }
                    }
                }
            }
        }
    }
}

#endif


#if ( _TEST_SELECT == 1 )
/**             select监听标准键盘输入
 */
int main()
{
    int sfd = 0;
    fd_set fd;

    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 1000;

    char buf[512] = {0};

    while (1)
    {

        /*句柄的清空和集合的设置放到循环里面执行，每次select返回时重新设置*/

        // fdset初始化为空集合
        FD_ZERO(&fd);

        // 在集合中添加fd传递的描述符
        FD_SET(sfd, &fd);

        int res = select(sfd+1, &fd, NULL, NULL, NULL);
        if ( res > 0 )
        {

            // 判断sfd是否为fd文件描述符集合里面的成员
            if ( FD_ISSET(sfd, &fd) )
            {
                printf("Recv data: ");
                fgets(buf, 512, stdin);
                printf("%s\n\n", buf);
            }

        }
        else if ( res == 0 )
        {
            printf("timeout.\n");

        }else
        {
            perror("select error.\n");
        }

    }

    return 0;
}

#endif
